home *** CD-ROM | disk | FTP | other *** search
/ L' Effet Pommier 3 / L'Effet Pommier - Volume 03.iso / Programmation / gray image 2.1 / write_tiff.cc < prev    next >
Text File  |  1995-05-30  |  13KB  |  404 lines

  1. // This may look like C code, but it is really -*- C++ -*-
  2. /*
  3.  ************************************************************************
  4.  *
  5.  *               Grayscale Image
  6.  *
  7.  *        Write an image into a file in the TIFF format
  8.  *
  9.  * The present TIFF writer creates a Class G TIFF file (for gray-scale
  10.  * images), See Appendix G of the TIFF specification. 
  11.  *
  12.  * The program writes the following required tags
  13.  *    NewSubfileType
  14.  *    ImageWidth
  15.  *    ImageLength
  16.  *    RowsPerStrip    (To make strips around 8K)
  17.  *    StripOffsets
  18.  *    StripByteCounts
  19.  *    XResolution    (72 dpi)
  20.  *    YResolution    (72 dpi)
  21.  *    ResolutionUnit
  22.  *
  23.  * In addition, the class G TIFF files are required to have the following
  24.  * tags with the following values
  25.  *    SamplesPerPixel = 1
  26.  *    BitsPerSample   = 4 or 8
  27.  *    Compression    = 1 or 5(LZW)
  28.  *    PhotometricInterpretation = 0 or 1
  29.  *
  30.  * In addition, if the image has name, it's written under
  31.  *     ImageDescription,
  32.  * The title, if specified, is written as
  33.  *    DocumentName
  34.  * tag
  35.  *
  36.  * $Id: write_tiff.cc,v 2.0 1995/03/06 21:55:06 oleg Exp oleg $
  37.  *
  38.  ************************************************************************
  39.  */
  40.  
  41. #include "image.h"
  42. #include "endian_io.h"
  43. #pragma implementation "tiff.h"
  44. #include "tiff.h"
  45.  
  46.                 // Write a TIFF header into the file
  47. void TIFFHeader::write(EndianOut& file)
  48. {
  49.   if( magic == TIFF_BIGENDIAN )
  50.     file.set_bigendian();
  51.   else if( magic == TIFF_LITTLEENDIAN )
  52.     file.set_littlendian();
  53.   else
  54.     _error("Unknown magic word %x",magic);
  55.   file.write_short(magic);
  56.   file.write_short(version);
  57.   file.write_long(diroffset);
  58. }
  59.  
  60.  
  61. /*
  62.  *------------------------------------------------------------------------
  63.  *                TIFF Directory
  64.  * Note that all items in the TIFF directory are threaded in an ascending
  65.  * order of their tags. So, when a new DirectoryItem is created, it inserts
  66.  * itself into the thread at proper position.
  67.  * Note, if some directory item has count field equal 0, the item is
  68.  * considered "dummy" and it's not written to the TIFF directory in the file.
  69.  *
  70.  */
  71.  
  72. class TIFFDirectoryItem : public TIFFDirEntry
  73. {
  74.   static int no_entries;        // So all TIFFDirEntry's got threaded
  75.   static TIFFDirectoryItem * entries[250]; // through in an ascending order 
  76.  
  77. protected:
  78.   virtual void write(EndianOut& file) = 0;    // Write this field into a file
  79.   virtual void write_value(EndianOut& file) = 0;// Write additional data
  80.  
  81. public:
  82.   TIFFDirectoryItem(const short _tag, const DataType _type, const long _count,
  83.             const long _value);
  84.   void write_all(EndianOut& file);    // Write the entire TIFF directory
  85. };
  86.  
  87.  
  88. int TIFFDirectoryItem::no_entries = 0;
  89. TIFFDirectoryItem * TIFFDirectoryItem::entries[250];
  90.  
  91.                 // Insert an item into the directory
  92.                 // in an ascending order of its tag
  93. TIFFDirectoryItem::TIFFDirectoryItem
  94.     (const short _tag, const DataType _type, const long _count,
  95.      const long _value)
  96.     : TIFFDirEntry(_tag,_type,_count,_value)
  97. {
  98.   if( no_entries >= sizeof(entries)/sizeof(entries[0]) )
  99.     _error("Can't add a new entry, the directory is full, %d items",
  100.        no_entries);
  101.   register int pos;            // Find a position where the present 
  102.   for(pos=0; pos<no_entries; pos++)    // item belongs (according to its tag)
  103.     if( entries[pos]->tag > tag )
  104.       break;
  105.   register int i;            // Move items with bigger tags toward
  106.   for(i=no_entries-1; i >= pos; i--)    // the end of the array to make space
  107.     entries[i+1] = entries[i];        // for our item
  108.   entries[pos] = this;
  109.   no_entries++;
  110. }
  111.  
  112.                 // Write the entire directory and clean up
  113. void TIFFDirectoryItem::write_all(EndianOut& file)
  114. {
  115.   int no_nondummy_items = 0;
  116.   register int i;
  117.   for(i=0; i<no_entries; i++)        // Count non-dummy dir entries
  118.     if( entries[i]->count != 0 )
  119.       no_nondummy_items++;
  120.   assert( no_nondummy_items > 0 );
  121.  
  122.   file.write_short(no_nondummy_items);
  123.   for(i=0; i<no_entries; i++)
  124.     if( entries[i]->count != 0 )
  125.       entries[i]->write(file);
  126.   file.write_long(0);            // Specify this is the end of the IFD
  127.                     // no other IFD is to follow
  128.   for(i=0; i<no_entries; i++)        // Handle items that store their values
  129.     if( entries[i]->count != 0 )    // after the directory
  130.       entries[i]->write_value(file);
  131.   no_entries = 0;
  132. }
  133.  
  134.  
  135. /*
  136.  *------------------------------------------------------------------------
  137.  *            Particular types of DirectoryItems
  138.  */
  139.  
  140.                 // Scalar item. It keeps its value in the
  141.                 // val_offset field of the DirItem
  142.                 // Note, values shorter than long are
  143.                 // LEFT-justified
  144. class ScalarTIFFDE : public TIFFDirectoryItem
  145. {
  146. protected:
  147.   void write(EndianOut& file);        // Write this field into a file
  148.   void write_value(EndianOut& file) {}    // Value is a part of the item, and
  149.                     // is written along with the item
  150.  
  151. public:
  152.   ScalarTIFFDE(const short _tag, const TSHORT _value)
  153.     : TIFFDirectoryItem(_tag,SHORT,1,(unsigned long)_value << 16) {}
  154.   ScalarTIFFDE(const short _tag, const TLONG _value)
  155.     : TIFFDirectoryItem(_tag,LONG,1,_value) {}
  156. };
  157.  
  158.                 // Write this item into a file
  159. void ScalarTIFFDE::write(EndianOut& file)
  160. {
  161.   file.write_short(tag);
  162.   file.write_short(type);
  163.   file.write_long(count);
  164.   file.write_long(val_offset);
  165. }
  166.  
  167.  
  168.                 // String item, the string itself is written
  169.                 // after the IFD, the val_offset field of
  170.                 // the item stores an offset to the string
  171.                 // characters.
  172. class StringTIFFDE : public TIFFDirectoryItem
  173. {
  174. protected:
  175.   void write(EndianOut& file);        // Write this field into a file
  176.   void write_value(EndianOut& file);    // Write the body of the string
  177.                     // (after the directory is written)
  178.  
  179.   streampos offset_for_val;        // for the val_offset field
  180.  
  181. public:
  182.   StringTIFFDE(const short _tag, const char * str)
  183.     : TIFFDirectoryItem(_tag,ASCII, str == 0 || str[0] == '\0' ? //empty string
  184.             0 : strlen(str)+1,(long)str) {}
  185. };
  186.  
  187.                 // Write this item into a file
  188. void StringTIFFDE::write(EndianOut& file)
  189. {
  190.   assert( count > 0 );
  191.   file.write_short(tag);
  192.   file.write_short(type);
  193.   file.write_long(count);
  194.   offset_for_val = file.tellp();
  195.   file.write_long(0);            // write a dummy for now
  196. }
  197.  
  198.                 // Write the body of the string,
  199.                 // Note, a padding may be necessary to
  200.                 // make the string end at a word boundary
  201. void StringTIFFDE::write_value(EndianOut& file)
  202. {
  203.   streamoff pos = file.tellp();        // where are we now
  204.                     // Write a body of the string, '\0'
  205.                     // and (possible) an extra byte
  206.   file.write((char *)val_offset,(strlen((char *)val_offset)+1+1) & ~1);
  207.                         // to keep word-boundary align
  208.   streampos pos_new = file.tellp();
  209.   assert( file.seekp(offset_for_val).good() );
  210.   file.write_long(pos);            // Write the offset within the dir elem
  211.   assert( file.seekp(pos_new).good() );    // Restore the file position 
  212.                     // to continue writing
  213. }
  214.  
  215.  
  216.                 // Directory item that refers to an array
  217.                 // (that can contain strip offsets, strip
  218.                 // byte counts, or other info specific to
  219.                 // each strip)
  220. class ArrayTIFFDE : public TIFFDirectoryItem
  221. {
  222. protected:
  223.   virtual void write(EndianOut& file);    // Write this field into a file
  224.   virtual void write_value(EndianOut& file);    // Write the body of the array
  225.                     // (after the directory is written)
  226.  
  227.   streampos offset_for_val;        // for the val_offset field
  228.   long * array;
  229.   
  230. public:
  231.   ArrayTIFFDE(const short _tag, const long no_elems);
  232.   virtual ~ArrayTIFFDE(void);
  233.   long& operator [] (const int index)    // Get hold of an element of the array
  234.       { assert( index >= 0 && index < count ); return array[index]; }
  235. };
  236.  
  237.                 // Constructor - allocate the array
  238. ArrayTIFFDE::ArrayTIFFDE(const short _tag, const long no_elems) 
  239.     : TIFFDirectoryItem(_tag, LONG, no_elems, 0)
  240. {
  241.   assert( count > 0 );
  242.   array = new long[count];
  243. }
  244.  
  245.  
  246.                 // Dispose of the array
  247. ArrayTIFFDE::~ArrayTIFFDE(void)
  248. {
  249.   assert( array != 0 );
  250.   delete [] array;
  251.   array = 0;
  252. }
  253.  
  254.  
  255.                 // Write this item into a file
  256. void ArrayTIFFDE::write(EndianOut& file)
  257. {
  258.   assert( count > 0 );
  259.   file.write_short(tag);
  260.   file.write_short(type);
  261.   file.write_long(count);
  262.   offset_for_val = file.tellp();
  263.   file.write_long(0);            // write a dummy for now
  264. }
  265.  
  266.                 // Write array elements (if more than 1)
  267.                 // after the directory
  268. void ArrayTIFFDE::write_value(EndianOut& file)
  269. {
  270.   long int pos;                // Where the array is to be written
  271.   register int i;
  272.   if( count == 1 )            // If there is only one elem, keep
  273.     pos = array[0];            // the offset with the dir elem itself
  274.   else
  275.   {
  276.     pos = file.tellp();            // Write the array separately
  277.     for(i=0; i<count; i++)
  278.       file.write_long(array[i]);
  279.   }
  280.   streampos pos_new = file.tellp();
  281.   assert( offset_for_val != 0 && file.seekp(offset_for_val).good() );
  282.   file.write_long(pos);            // Write the offset within the dir elem
  283.   assert( file.seekp(pos_new).good() );    // Restore the file position 
  284.                     // to continue writing
  285. }
  286.  
  287.                 // Rational number: an array of two LONGs
  288. class RationalTIFFDE : public ArrayTIFFDE
  289. {
  290.   virtual void write(EndianOut& file);    // Write this field into a file
  291. public:
  292.   RationalTIFFDE(const short _tag, const int numerator,const int denominator)
  293.     : ArrayTIFFDE(_tag,2)
  294.     { array[0] = numerator; array[1] = denominator; type = RATIONAL; }
  295. };
  296.  
  297.                 // Write this item into a file: only one
  298.                 // modification: a rational has count 1, though
  299.                 // it's an array of 2 longs
  300. void RationalTIFFDE::write(EndianOut& file)
  301. {
  302.   assert( count == 2 );
  303.   count = 1;                // temporarily: that's what we write
  304.   ArrayTIFFDE::write(file);        // into the tag
  305.   count = 2;
  306. }
  307.  
  308.  
  309.                 // Strip item, that refers to an array
  310.                 // that contains offsets to the strips (set
  311.                 // of rows) of the image
  312.                 // No compression is used at present
  313. class StripTIFFDE : public ArrayTIFFDE
  314. {
  315. protected:
  316.   void write_value(EndianOut& file);    // Write the array of strip ptrs
  317.                     // and strips themselves
  318.                     // (after the directory is written)
  319.  
  320.   ArrayTIFFDE    strip_byte_counts;    // It is not necessary unless compress-
  321.                     // ion is used; but neverthelesss
  322.   short rows_per_strip;
  323.   const IMAGE& image;
  324.  
  325. public:
  326.   StripTIFFDE(const short _no_strips, const short _rows_per_strip,
  327.           const IMAGE& _image)
  328.     : ArrayTIFFDE(TIFFTAG_STRIPOFFSETS,_no_strips),
  329.       strip_byte_counts(TIFFTAG_STRIPBYTECOUNTS,_no_strips),
  330.       rows_per_strip(_rows_per_strip), image(_image) {}
  331. };
  332.  
  333.                     // Write the array of strip ptrs
  334.                     // and strips themselves
  335.                     // (after the directory is written)
  336.                     // Note the trick! StripByteCount
  337.                     // has a bigger tag, i.e. strip_byte_
  338.                     // count will be written AFTER the
  339.                     // strips are written, i.e., when
  340.                     // strip sizes are already known
  341. void StripTIFFDE::write_value(EndianOut& file)
  342. {
  343.   assert( count > 0 );
  344.   register int strip;
  345.   for(strip=0; strip<count; strip++)
  346.   {
  347.     array[strip] = file.tellp();    // where are we now, beg of a strip
  348.     register int i,j;            // Writing a strip
  349.     for(i=strip*rows_per_strip; 
  350.     i<(strip+1)*rows_per_strip && i < image.q_nrows(); i++)
  351.       for(j=0; j<image.q_ncols(); j++)
  352.     file.write_byte(image(i,j));
  353.     strip_byte_counts[strip] = file.tellp() - array[strip];
  354.   }
  355.   ArrayTIFFDE::write_value(file);
  356. }
  357.  
  358. /*
  359.  *------------------------------------------------------------------------
  360.  *                Root module
  361.  */
  362.  
  363. void IMAGE::write_tiff(const char * file_name,const char * title) const
  364. {
  365.   is_valid();
  366.  
  367.   message("\nPreparing a TIFF file with name '%s'\n",file_name);
  368.  
  369.   EndianOut file(file_name);
  370.  
  371.   TIFFHeader header;
  372.   header.write(file);
  373.  
  374.   ScalarTIFFDE subfile_tag(TIFFTAG_SUBFILETYPE,(unsigned)0);
  375.   ScalarTIFFDE width_tag(TIFFTAG_IMAGEWIDTH,(unsigned)q_ncols());
  376.   ScalarTIFFDE length_tag(TIFFTAG_IMAGELENGTH,(unsigned)q_nrows());
  377.   ScalarTIFFDE compr_tag(TIFFTAG_COMPRESSION,(unsigned short)COMPRESSION_NONE);
  378.   ScalarTIFFDE phot_tag(TIFFTAG_PHOTOMETRIC,
  379.             (unsigned short)PHOTOMETRIC_MINISBLACK);
  380.   ScalarTIFFDE resunit_tag(TIFFTAG_RESOLUTIONUNIT,
  381.                (unsigned short)RESUNIT_INCH);
  382.                     // Means 72 pixels per unit (inch)
  383.   RationalTIFFDE xres_tag(TIFFTAG_XRESOLUTION,72,1);
  384.   RationalTIFFDE yres_tag(TIFFTAG_YRESOLUTION,72,1);
  385.  
  386.   ScalarTIFFDE samplesize_tag(TIFFTAG_SAMPLESPERPIXEL,(unsigned short)1);
  387.   assert( bits_per_pixel == 8 );
  388.   ScalarTIFFDE depth_tag(TIFFTAG_BITSPERSAMPLE,(unsigned short)8);
  389.  
  390.  
  391.   StringTIFFDE desc_tag(TIFFTAG_IMAGEDESCRIPTION, name );
  392.   StringTIFFDE docname_tag(TIFFTAG_DOCUMENTNAME, title );
  393.  
  394.   const int strip_target_size = 8*1024;
  395.   unsigned int rows_per_strip = strip_target_size / q_ncols();
  396.   int no_strips = (q_nrows() + rows_per_strip - 1)/rows_per_strip;
  397.   ScalarTIFFDE striprs_tag(TIFFTAG_ROWSPERSTRIP,rows_per_strip);
  398.   StripTIFFDE  stripoffs_tag(no_strips, rows_per_strip, *this);
  399.  
  400.   subfile_tag.write_all(file);            
  401.   file.close();
  402. }
  403.  
  404.